using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using GameFramework;
using GameFramework.Event;
using GameFramework.Network;
using Pitaya.SimpleJson;
using UnityEngine;
using UnityGameFramework.Runtime;

namespace StarForce
{
    // 协议号= https://github.com/lonng/nano/blob/master/docs/communication_protocol.md
    
    
    public class NetWorkHelp:INetworkChannelHelper
    {
        private INetworkChannel m_NetworkChannel = null;
        // 包头
        readonly byte[] headBuffer = new byte[4];
        // 心跳
        private C2SHeartBeat m_C2SHeartBeat = new C2SHeartBeat();
        // 路由字典
        private Dictionary<uint, string> reqMap;
        
        private JsonObject emptyMsg = new JsonObject();
        // 发送包参数
        public const int MSG_Route_Limit = 255;
        public const int MSG_Route_Mask = 0x01; // 路由压缩，暂未用到
        public const int MSG_Type_Mask = 0x07;
        
        public uint reqId = 1;
        private EventManagerString eventManager;
        
        // handler 处理，暂时没用
        private readonly Dictionary<PackageType, Type> m_ServerToClientPacketTypes = new Dictionary<PackageType, Type>();
        
        public void Initialize(INetworkChannel networkChannel)
        {
            m_NetworkChannel = networkChannel;
            
            this.reqMap = new Dictionary<uint, string>();
            eventManager = new EventManagerString();
            // 反射注册包和包处理函数。
            Type packetBaseType = typeof(S2CPacketBase);
            Type packetHandlerBaseType = typeof(PacketHandlerBase);
            Assembly assembly = Assembly.GetExecutingAssembly();
            Type[] types = assembly.GetTypes();
            for (int i = 0; i < types.Length; i++)
            {
                if (!types[i].IsClass || types[i].IsAbstract)
                {
                    continue;
                }

                if (types[i].BaseType == packetBaseType)
                {
                    PacketBase packetBase = (PacketBase)Activator.CreateInstance(types[i]);
                    Type packetType = GetServerToClientPacketType(packetBase.Flag);
                    if (packetType != null)
                    {
                        Log.Warning("Already exist packet type '{0}', check '{1}' or '{2}'?.", packetBase.Id.ToString(), packetType.Name, packetBase.GetType().Name);
                        continue;
                    }

                    m_ServerToClientPacketTypes.Add(packetBase.Flag, types[i]);
                }
                else if (types[i].BaseType == packetHandlerBaseType)
                {
                    IPacketHandler packetHandler = (IPacketHandler)Activator.CreateInstance(types[i]);
                    m_NetworkChannel.RegisterHandler(packetHandler);
                }
            }

            
            Log.Info("网络连接构造函数");
            GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkConnectedEventArgs.EventId, OnNetworkConnected);
            GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkClosedEventArgs.EventId, OnNetworkClosed);
            GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkMissHeartBeatEventArgs.EventId, OnNetworkMissHeartBeat);
            GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkErrorEventArgs.EventId, OnNetworkError);
            GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkCustomErrorEventArgs.EventId, OnNetworkCustomError);
        }
        
        private void OnNetworkCustomError(object sender, GameEventArgs e)
        {
            Log.Info("OnNetworkCustomError 自定义网络错误");
        }

        private void OnNetworkError(object sender, GameEventArgs e)
        {
            UnityGameFramework.Runtime.NetworkErrorEventArgs eventArgs =
                (UnityGameFramework.Runtime.NetworkErrorEventArgs) e;
            Log.Info($"OnNetworkError 网络错误={eventArgs.ErrorMessage}");
        }

        private void OnNetworkMissHeartBeat(object sender, GameEventArgs e)
        {
            Log.Info("OnNetworkMissHeartBeat 心跳包错误");
        }

        private void OnNetworkClosed(object sender, GameEventArgs e)
        {
            Log.Info("OnNetworkClosed 连接关闭");
            if (eventManager != null)
            {
                eventManager.Dispose();
            }
        }

        private void OnNetworkConnected(object sender, GameEventArgs e)
        {
            Log.Info("OnNetworkConnected 已建立连接");
            // 发送握手包
            m_NetworkChannel.Send(new C2SHandShake());
        }

        public void Shutdown()
        {
            Log.Info("网络连接关闭");
            GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkConnectedEventArgs.EventId, OnNetworkConnected);
            GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkClosedEventArgs.EventId, OnNetworkClosed);
            GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkMissHeartBeatEventArgs.EventId, OnNetworkMissHeartBeat);
            GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkErrorEventArgs.EventId, OnNetworkError);
            GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkCustomErrorEventArgs.EventId, OnNetworkCustomError);

        }

        public void PrepareForConnecting()
        {
            Log.Info("准备网络连接");
        }

        public bool SendHeartBeat()
        {
            Log.Info("【发包】发送心跳包");
            m_NetworkChannel.Send(m_C2SHeartBeat);
            return true;
        }

        public bool Serialize<T>(T packet, Stream destination) where T : Packet
        {
            
            PacketBase packetImpl = packet as PacketBase;
            if (packetImpl == null)
            {
                Log.Warning("发送的包是为空");
                return false;
            }

            if (packetImpl.PacketType != PacketType.ClientToServer)
            {
                Log.Warning("发送的包不是 ClientToServer");
                return false;
            }
            // 设置当前流的位置为流的开始 
            destination.Seek(0, SeekOrigin.Begin); 

            PackageType packageType = packetImpl.Flag;
            Log.Info($"【发包】=>{packageType}");
            switch (packageType)
            {
                case PackageType.PKG_HANDSHAKE:
                    C2SHandShake c2SHandShake = (C2SHandShake)packetImpl;
                    byte[] handShake = PackageProtocol.encode(packageType, c2SHandShake.HandShake);
                    destination.Write(handShake,0,handShake.Length);
                    break;
                case PackageType.PKG_HANDSHAKE_ACK:
                    byte[] handShakeACK = PackageProtocol.encode(packageType, new byte[0]);
                    destination.Write(handShakeACK,0,handShakeACK.Length);
                    break;
                case PackageType.PKG_HEARTBEAT:
                    byte[] heartBeat = PackageProtocol.encode(PackageType.PKG_HEARTBEAT);
                    destination.Write(heartBeat,0,heartBeat.Length);
                    break;
                case PackageType.PKG_DATA:
                    C2SData c2SData = (C2SData)packetImpl;
                    
                    int routeLength = byteLength(c2SData.route);
                    if (routeLength > MSG_Route_Limit)
                    {
                        throw new Exception("Route is too long!");
                    }

                    //Encode head
                    //The maximus length of head is 1 byte flag + 4 bytes message id + route string length + 1byte
                    byte[] head = new byte[routeLength + 6];
                    int offset = 1;
                    byte flag = 0;
            
                    if (c2SData.id > 0)
                    {
                        byte[] bytes = Encoder.encodeUInt32(c2SData.id);

                        writeBytes(bytes, offset, head);
                        flag |= ((byte)MessageType.MSG_REQUEST) << 1;
                        offset += bytes.Length;
                    }
                    else
                    {
                        flag |= ((byte)MessageType.MSG_NOTIFY) << 1;
                    }
            
                    // head
                    //Write route length
                    head[offset++] = (byte)routeLength;

                    //Write route
                    writeBytes(Encoding.UTF8.GetBytes(c2SData.route), offset, head);
                    offset += routeLength;
                    // 
                    head[0] = flag;

                    //Encode body
                    byte[] body = Encoding.UTF8.GetBytes(c2SData.msg);
            
                    //Construct the result
                    byte[] result = new byte[offset + body.Length];
                    for (int i = 0; i < offset; i++)
                    {
                        result[i] = head[i];
                    }

                    for (int i = 0; i < body.Length; i++)
                    {
                        result[offset + i] = body[i];
                    }
                    //Add id to route map
                    byte[] pkgdata = PackageProtocol.encode(packageType, result);
                    destination.Write(pkgdata,0,pkgdata.Length);
                    ReferencePool.Release(c2SData);
                    break;
            }

            return true;
        }

        public IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData)
        {
            customErrorData = null;
            S2CPacketHeader s2CPacketHeader = ReferencePool.Acquire<S2CPacketHeader>();
            source.Read(headBuffer,0,headBuffer.Length);
            // 设置当前流的位置为流的开始 
            source.Seek(0, SeekOrigin.Begin); 
            
            //Get flag
            byte flag = headBuffer[0];
            
            //
            PackageType pkgType = (PackageType)flag;
            int pkgLength = (headBuffer[1] << 16) + (headBuffer[2] << 8) + headBuffer[3];
           
            s2CPacketHeader.Flag = pkgType;
            s2CPacketHeader.PacketLength = pkgLength;
            
            return s2CPacketHeader;
        }

        public Packet DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData)
        {
            customErrorData = null;
            S2CPacketHeader s2CPacketHeader = (S2CPacketHeader)packetHeader;
            if (s2CPacketHeader==null)
            {
                Debug.Log("没有包头");
            }
            Packet packet = null;
            if (s2CPacketHeader != null && s2CPacketHeader.IsValid)
            {
                byte[] bytes = new byte[source.Length]; 
                source.Read(bytes, 0, bytes.Length); 
                // 设置当前流的位置为流的开始 
                source.Seek(0, SeekOrigin.Begin);

                // 包
                string jsonStr = Encoding.UTF8.GetString(bytes);
                
                JsonObject data = null;
                switch (s2CPacketHeader.Flag)
                {
                    case PackageType.PKG_DATA:
                        
                        int offset = 1;
                        // 消息类型
                        MessageType type = (MessageType)((bytes[0] >> 1) & MSG_Type_Mask);
                        
                        // 消息 id
                        uint id = 0;
                        string route;
                        if (type == MessageType.MSG_RESPONSE)
                        {
                            int length;
                            id = (uint)Decoder.decodeUInt32(offset, bytes, out length);
                            if (id <= 0 || !reqMap.ContainsKey(id))
                            {
                                // 忽略
                                Debug.Log($"【收包】 包消息类型={type} id={id} 包={jsonStr}");
                            }
                            else
                            {
                                route = reqMap[id];
                                reqMap.Remove(id);
                                // 回调
                                eventManager.InvokeCallBack(id, jsonStr);
                                Debug.Log($"【收包】 包消息类型={type} id={id} route={route} 包={jsonStr}");
                            }
                        }
                        else if (type == MessageType.MSG_PUSH)
                        {
                            byte length = bytes[offset];
                            offset += 1;

                            route = Encoding.UTF8.GetString(bytes, offset, length);
                            string replace = jsonStr.Replace(route, "");
                            // 回调
                            eventManager.InvokeOnEvent(route, replace);
                            Debug.Log($"【收包】 包消息类型={type} id={id} route={route} 包={replace}");
                        }
                        else
                        {
                            Debug.LogError("【收包】消息类型错误");
                        }

                        // packet = new S2CData();
                        break;
                    case PackageType.PKG_KICK:
                        // packet = new S2CKick();
                        Debug.Log($"【收包】 包消息类型={s2CPacketHeader.Flag}");
                        break;
                    case PackageType.PKG_HANDSHAKE:
                        // packet = new S2CHandShake();
                        Debug.Log($"【收包】 包消息类型={s2CPacketHeader.Flag}");
                        
                        data = (JsonObject)SimpleJson.DeserializeObject(jsonStr);
                        // 收到握手包  {"code":200,"sys":{"heartbeat":30}}
                        if (data.ContainsKey("code") && data.ContainsKey("sys"))
                        {
                            JsonObject sys = (JsonObject)data["sys"];
                            if (sys.ContainsKey("heartbeat"))
                            {
                                int heartBeatInterval = Convert.ToInt32(sys["heartbeat"]);
                                m_NetworkChannel.HeartBeatInterval = heartBeatInterval;
                                Debug.Log($"设置心跳为={heartBeatInterval}");
                                // 握手包 ack
                                m_NetworkChannel.Send(new C2SHandShakeACK());
                            }
                        }

                        
                        break;
                    case PackageType.PKG_HEARTBEAT:
                        Debug.Log($"【收包】 包消息类型={s2CPacketHeader.Flag}");
                        // packet = new S2CHeartBeat();
                        break;
                    case PackageType.PKG_HANDSHAKE_ACK:
                        Debug.Log($"【收包】 包消息类型={s2CPacketHeader.Flag}");
                        // packet = new S2CHandShakeAck();
                        break;
                }
                
            }
            else
            {
                Debug.LogError("包头不正常");
            }
            ReferencePool.Release(s2CPacketHeader);
            return packet;
        }

        public int PacketHeaderLength => PackageProtocol.HEADER_LENGTH;
        
        private Type GetServerToClientPacketType(PackageType flag)
        {
            Type type = null;
            if (m_ServerToClientPacketTypes.TryGetValue(flag, out type))
            {
                return type;
            }

            return null;
        }

        
        public void Request(string route, string msg, Action<string> action)
        {
            C2SData c2SData = ReferencePool.Acquire<C2SData>();
            c2SData.id = reqId;
            c2SData.route = route;
            c2SData.msg = msg;
            c2SData.msgType = MessageType.MSG_REQUEST;
            //Add id to route map
            if (reqId > 0)
            {
                reqMap.Add(reqId, route);
            }
            this.eventManager.AddCallBack(reqId, action);
            m_NetworkChannel.Send(c2SData);
            reqId++;
        }
        
        public void Request(string route, Action<string> action)
        {
            C2SData c2SData = ReferencePool.Acquire<C2SData>();
            c2SData.id = reqId;
            c2SData.route = route;
            c2SData.msg = emptyMsg.ToString();
            c2SData.msgType = MessageType.MSG_REQUEST;
            //Add id to route map
            if (reqId > 0)
            {
                reqMap.Add(reqId, route);
            }
            this.eventManager.AddCallBack(reqId, action);
            m_NetworkChannel.Send(c2SData);
            reqId++;
        }

        public void Notify(string route, string msg)
        {
            C2SData c2SData = ReferencePool.Acquire<C2SData>();
            c2SData.id = 0;
            c2SData.route = route;
            c2SData.msg = msg;
            c2SData.msgType = MessageType.MSG_REQUEST;
            
            m_NetworkChannel.Send(c2SData);
        }

        public void On(string eventName, Action<string> action)
        {
            eventManager.AddOnEvent(eventName, action);
        }
        
        private int byteLength(string msg)
        {
            return Encoding.UTF8.GetBytes(msg).Length;
        }
        
        private void writeBytes(byte[] source, int offset, byte[] target)
        {
            for (int i = 0; i < source.Length; i++)
            {
                target[offset + i] = source[i];
            }
        }
        
        private ushort readShort(int offset, byte[] bytes)
        {
            ushort result = 0;

            result += (ushort)(bytes[offset] << 8);
            result += (ushort)(bytes[offset + 1]);

            return result;
        }
    }
}